﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.Serialization.Formatters.Binary;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using System.Xml;
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
using System.ComponentModel.DataAnnotations;

namespace Yunlib.Common
{
    public static class CustomExtensions
    {
        #region 字符串扩展

        public static string TrimNbsp(this string value)
        {
            if (string.IsNullOrWhiteSpace(value))
                return string.Empty;
            else
                return value.Replace("&nbsp;", "").Trim();
        }

        public static String SQuote(this String s)
        {
            if (string.IsNullOrWhiteSpace(s)) return "''";
            int len = s.Length + 25;
            StringBuilder tmpS = new StringBuilder(len); // hopefully only one alloc
            tmpS.Append("N'");
            tmpS.Append(s.Replace("'", "''"));
            tmpS.Append("'");
            return tmpS.ToString();
        }

        /// <summary>
        /// 是否为空
        /// </summary>
        /// <param name="value">值</param>
        public static bool IsEmpty(this string value)
        {
            return string.IsNullOrWhiteSpace(value);
        }
        /// <summary>
        /// 是否为空
        /// </summary>
        /// <param name="value">值</param>
        public static bool IsEmpty(this Guid? value)
        {
            if (value == null)
                return true;
            return IsEmpty(value.Value);
        }
        /// <summary>
        /// 是否为空
        /// </summary>
        /// <param name="value">值</param>
        public static bool IsEmpty(this Guid value)
        {
            if (value == Guid.Empty)
                return true;
            return false;
        }
        /// <summary>
        /// 是否为空
        /// </summary>
        /// <param name="value">值</param>
        public static bool IsEmpty(this object value)
        {
            if (value != null && !string.IsNullOrEmpty(value.ToString()))
            {
                return false;
            }
            else
            {
                return true;
            }
        }

        /// <summary>
        /// 扩展数据方法
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="t"></param>
        /// <param name="condition"></param>
        /// <returns></returns>
        public static bool Contains<T>(this T[] t, Predicate<T> condition)
        {
            bool IsExisted = false;

            foreach (T item in t)
            {
                if (condition(item))
                {
                    IsExisted = true;
                    break;
                }
            }

            return IsExisted;
        }

        /// <summary>
        /// 字符串是否为空或空白字符
        /// </summary>
        /// <returns></returns>
        [DebuggerStepThrough]
        public static bool IsNullOrWhiteSpace(this String Str)
        {
            return Str == null || Str.Length == 0 || Str.Trim().Length == 0;
        }

        /// <summary>
        /// StringFormat扩展
        /// </summary>
        /// <param name="Str"></param>
        /// <param name="param"></param>
        /// <returns></returns>
        [DebuggerStepThrough]
        public static string FormatWith(this String Str, params object[] param)
        {
            if (Str.IsNullOrWhiteSpace())
            {
                string str = string.Empty;

                param.Each(s => str += s + "");

                return str;
            }

            return String.Format(Str, param);
        }

        /// <summary>
        /// 替换sql中的引号
        /// </summary>
        /// <param name="s"></param>
        /// <returns></returns>
        public static String SqlQuote(this String s)
        {
            int len = s.Length + 25;
            StringBuilder tmpS = new StringBuilder(len); // hopefully only one alloc
            tmpS.Append("N'");
            tmpS.Append(s.Replace("'", "''"));
            tmpS.Append("'");
            return tmpS.ToString();
        }

        /// <summary>
        /// base64编码
        /// </summary>
        /// <param name="s"></param>
        /// <returns></returns>
        public static string Base64Encode(this string s)
        {
            return Convert.ToBase64String(Encoding.UTF8.GetBytes(s));
        }

        /// <summary>
        /// base64解码
        /// </summary>
        /// <param name="s"></param>
        /// <returns></returns>
        public static string Base64Decode(this string s)
        {
            return Encoding.UTF8.GetString(Convert.FromBase64String(s));
        }


        public static bool IsMatch(this string str, string op)
        {
            if (str.Equals(String.Empty) || str == null) return false;
            Regex re = new Regex(op, RegexOptions.IgnoreCase);
            return re.IsMatch(str);
        }

        public static bool IsMobile(this string input)
        {
            return input.IsMatch(@"^1[3,4,5,6,7,8,9]\d{9}$");
        }

        /// <summary>
        /// 判断字符串中是否包含中文
        /// </summary>
        /// <param name="str">需要判断的字符串</param>
        /// <returns>判断结果</returns>
        public static bool HasChinese(this string str)
        {
            return Regex.IsMatch(str, @"[\u4e00-\u9fa5]");
        }

        public static List<T> SplitToEntity<T>(this string value,Func<string,T> method, char splitChar = ',')
        {
            List<T> list = new List<T>();

            if (!value.IsEmpty())
            {
                string[] ids = value.Split(new char[] { splitChar }, StringSplitOptions.RemoveEmptyEntries);
                ids.Each(i => list.Add(method(i)));
            }

            return list;
        }

        /// <summary>  
        /// 判断一个字符串是否为合法整数(不限制长度)  
        /// </summary>  
        /// <param name="s">字符串</param>  
        /// <returns></returns>  
        public static bool IsInteger(this string s)  
        {  
            string pattern = @"^\d*$";  
            return Regex.IsMatch(s,pattern);  
        }  
        /**//// <summary>  
        /// 判断一个字符串是否为合法数字(0-32整数)  
        /// </summary>  
        /// <param name="s">字符串</param>  
        /// <returns></returns>  
        public static bool IsNumber(this string s)  
        {  
            return IsNumber(s,32,0);  
        }  
        /**//// <summary>  
        /// 判断一个字符串是否为合法数字(指定整数位数和小数位数)  
        /// </summary>  
        /// <param name="s">字符串</param>  
        /// <param name="precision">整数位数</param>  
        /// <param name="scale">小数位数</param>  
        /// <returns></returns>  
        public static bool IsNumber(this string s, int precision, int scale)
        {
            if ((precision == 0) && (scale == 0))
            {
                return false;
            }
            string pattern = @"(^\d{1," + precision + "}";
            if (scale > 0)
            {
                pattern += @"\.\d{0," + scale + "}$)|" + pattern;
            }
            pattern += "$)";
            return Regex.IsMatch(s, pattern);
        }

        #endregion

        #region Xml序列化反序列化

        /// <summary>
        /// 序列化对象到指定目标类型字符串
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="obj"></param>
        /// <param name="Target"></param>
        /// <returns></returns>
        public static string XmlSerializer<T>(this T obj, Type Source = null, Type Target = null)
            where T : class , new()
        {
            if (obj == null) return string.Empty;

            if (Target == null || Source == null)
                return MethodHelper.Serializer(obj);
            else
            {
                string result = MethodHelper.Serializer(obj);
                result = result.Replace("ArrayOf{0}".FormatWith(Source.Name), "ArrayOf{0}".FormatWith(Target.Name));
                result = result.Replace("<{0}>".FormatWith(Source.Name), "<{0}>".FormatWith(Target.Name));
                result = result.Replace("</{0}>".FormatWith(Source.Name), "</{0}>".FormatWith(Target.Name));
                return result;
            }
        }

        /// <summary>
        /// 反序列化xml到指定对象
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="obj"></param>
        /// <returns></returns>
        public static T XmlDeserialize<T>(this XmlNode obj, Type Source = null, Type Target = null)
            where T : class , new()
        {
            if (obj == null) return null;

            return obj.OuterXml.XmlDeserialize<T>(Source, Target);
        }

        /// <summary>
        /// 反序列化xml到指定对象
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="obj"></param>
        /// <returns></returns>
        public static T XmlDeserialize<T>(this string obj, Type Source = null, Type Target = null)
           where T : class , new()
        {
            if (obj == null || obj.Length == 0 || obj.Trim().Length == 0) return new T();

            if (typeof(T).Name == "ReturnObject") throw new Exception("反序列化ReturnObject对象,请使用GetReturnObject()方法.");

            if (Target == null || Source == null)
                return MethodHelper.Deserialize<T>(obj);
            else
            {
                string result = obj;
                result = result.Replace("ArrayOf{0}".FormatWith(Source.Name), "ArrayOf{0}".FormatWith(Target.Name));
                result = result.Replace("<{0}>".FormatWith(Source.Name), "<{0}>".FormatWith(Target.Name));
                result = result.Replace("</{0}>".FormatWith(Source.Name), "</{0}>".FormatWith(Target.Name));
                result = result.Replace("&lt;{0}&gt;".FormatWith(Source.Name), "&lt;{0}&gt;".FormatWith(Target.Name));
                result = result.Replace("&lt;/{0}&gt;".FormatWith(Source.Name), "&lt;/{0}&gt;".FormatWith(Target.Name));
                return MethodHelper.Deserialize<T>(result); ;
            }
        }

        #endregion

        #region  对象扩展

        public static T ConvertTo<T>(this object value) { return value.ConvertTo(default(T)); }
        public static T ConvertTo<T>(this object value, T defaultValue)
        {
            if (value != null)
            {
                var targetType = typeof(T);

                var converter = TypeDescriptor.GetConverter(value);
                if (converter != null)
                {
                    if (converter.CanConvertTo(targetType)) return (T)converter.ConvertTo(value, targetType);
                }

                converter = TypeDescriptor.GetConverter(targetType);
                if (converter != null)
                {
                    try { if (converter.CanConvertFrom(value.GetType())) return (T)converter.ConvertFrom(value); }
                    catch { }
                }
            }
            return defaultValue;
        }
        public static T ConvertTo<T>(this object value, T defaultValue, bool ignoreException)
        {
            if (ignoreException)
            {
                try
                {
                    return value.ConvertTo<T>();
                }
                catch
                {
                    return defaultValue;
                }
            }
            return value.ConvertTo<T>();
        }

        public static int ToInt(this object strValue, int defValue) { int def = 0; int.TryParse(strValue.ToString(), out def); return def == 0 ? defValue : def; }
        public static byte ToTinyInt(this object strValue, byte defValue) { byte def = 0; byte.TryParse(strValue.ToString(), out def); return def == 0 ? defValue : def; }
        public static short ToSmallInt(this object strValue, short defValue) { short def = 0; short.TryParse(strValue.ToString(), out def); return def == 0 ? defValue : def; }
        public static decimal ToDecimal(this object strValue, decimal defValue) { decimal def = 0; decimal.TryParse(strValue.ToString(), out def); return def == 0 ? defValue : def; }
        public static float ToFloat(this object strValue, float defValue) { float def = 0; float.TryParse(strValue.ToString(), out def); return def == 0 ? defValue : def; }
        public static Int64 ToBigInt(this object strValue, Int64 defValue) { Int64 def = 0; Int64.TryParse(strValue.ToString(), out def); return def == 0 ? defValue : def; }
        public static decimal ToMoney(this object strValue, decimal defValue) { decimal def = 0; decimal.TryParse(strValue.ToString(), out def); return def == 0 ? defValue : def; }
        public static int ToInteger(this object strValue, int defValue) { int def = 0; int.TryParse(strValue.ToString(), out def); return def == 0 ? defValue : def; }
        public static bool ToBool(this object Expression, bool defValue)
        {
            if (Expression != null)
            {
                if (string.Compare(Expression.ToString(), "true", true) == 0) return true;
                if (string.Compare(Expression.ToString(), "false", true) == 0) return false;
                if (string.Compare(Expression.ToString(), "1", true) == 0) return true;
                if (string.Compare(Expression.ToString(), "0", true) == 0) return false;
                if (string.Compare(Expression.ToString(), "on", true) == 0) return true;
            }
            return defValue;
        }
        public static int ToInt(this object strValue) { return strValue.ToInt(0); }
        public static byte ToTinyInt(this object strValue) { return strValue.ToTinyInt(0); }
        public static short ToSmallInt(this object strValue) { return strValue.ToSmallInt(0); }
        public static decimal ToDecimal(this object strValue) { return strValue.ToDecimal(0); }
        public static float ToFloat(this object strValue) { return strValue.ToFloat(0); }
        public static Int64 ToBigInt(this object strValue) { return strValue.ToBigInt(0); }
        public static decimal ToMoney(this object strValue) { return strValue.ToMoney(0); }
        public static int ToInteger(this object strValue) { return strValue.ToInteger(0); }
        public static bool ToBool(this object strValue) { return strValue.ToBool(false); }

        public static object InvokeMethod(this object obj, string methodName, params object[] parameters)
        {
            return InvokeMethod<object>(obj, methodName, parameters);
        }
        public static T InvokeMethod<T>(this object obj, string methodName)
        {
            return InvokeMethod<T>(obj, methodName, null);
        }
        public static T InvokeMethod<T>(this object obj, string methodName, params object[] parameters)
        {
            var type = obj.GetType();
            var method = type.GetMethod(methodName);

            if (method == null) throw new ArgumentException(string.Format("Method '{0}' not found.", methodName), methodName);

            var value = method.Invoke(obj, parameters);
            return (value is T ? (T)value : default(T));
        }

        public static object GetPropertyValue(this object obj, string propertyName)
        {
            return GetPropertyValue<object>(obj, propertyName, null);
        }
        public static T GetPropertyValue<T>(this object obj, string propertyName)
        {
            return GetPropertyValue<T>(obj, propertyName, default(T));
        }
        public static T GetPropertyValue<T>(this object obj, string propertyName, T defaultValue)
        {
            var type = obj.GetType();
            var property = type.GetProperty(propertyName);

            if (property == null) throw new ArgumentException(string.Format("Property '{0}' not found.", propertyName), propertyName);

            var value = property.GetValue(obj, null);
            return (value is T ? (T)value : defaultValue);
        }
        public static void SetPropertyValue(this object obj, string propertyName, object value)
        {
            var type = obj.GetType();
            var property = type.GetProperty(propertyName);

            if (property == null) throw new ArgumentException(string.Format("Property '{0}' not found.", propertyName), propertyName);

            property.SetValue(obj, value, null);
        }

        public static T GetAttribute<T>(this object obj) where T : Attribute
        {
            return GetAttribute<T>(obj, true);
        }
        public static T GetAttribute<T>(this object obj, bool includeInherited) where T : Attribute
        {
            var type = (obj as Type ?? obj.GetType());
            var attributes = type.GetCustomAttributes(typeof(T), includeInherited);
            if ((attributes != null) && (attributes.Length > 0))
            {
                return (attributes[0] as T);
            }
            return null;
        }

        public static IEnumerable<T> GetAttributes<T>(this object obj) where T : Attribute
        {
            return GetAttributes<T>(obj);
        }
        public static IEnumerable<T> GetAttributes<T>(this object obj, bool includeInherited) where T : Attribute
        {
            var type = (obj as Type ?? obj.GetType());
            foreach (var attribute in type.GetCustomAttributes(typeof(T), includeInherited))
            {
                if (attribute is T) yield return (T)attribute;
            }
        }

        public static bool IsType(this object obj, Type type)
        {
            return obj.GetType().Equals(type);
        }
        public static T ToType<T>(this object value) { return (T)value; }
        public static bool IsArray(this object obj)
        {
            return obj.IsType(typeof(System.Array));
        }
        public static bool IsDBNull(this object obj)
        {
            return obj.IsType(typeof(DBNull));
        }

        public static byte[] Serialize(this object value)
        {
            MemoryStream ms = new MemoryStream();
            BinaryFormatter bf1 = new BinaryFormatter();
            bf1.Serialize(ms, value);
            return ms.ToArray();
        }

        public static void CheckOnNull(this object @this, string parameterName)
        {
            if (@this.IsNull()) throw new ArgumentNullException(parameterName);
        }
        public static void CheckOnNull(this object @this, string parameterName, string message)
        {
            if (@this.IsNull()) throw new ArgumentNullException(parameterName, message);
        }
        public static bool IsNull(this object @this)
        {
            return @this == null;
        }
        public static bool IsNotNull(this object @this)
        {
            return !@this.IsNull();
        }
        public static T UnsafeCast<T>(this object value)
        {
            return value.IsNull() ? default(T) : (T)value;
        }
        public static T SafeCast<T>(this object value)
        {
            return value is T ? value.UnsafeCast<T>() : default(T);
        }
        public static bool InstanceOf<T>(this object value)
        {
            return value is T;
        }

        public static T Clone<T>(this T o) where T : class
        {
            Type type = o.GetType().BaseType;

            MethodInfo[] methodInfos = type.GetMethods(BindingFlags.NonPublic
                            | BindingFlags.Instance
                            | BindingFlags.DeclaredOnly);
            MethodInfo cloneMethod = null;
            foreach (var item in methodInfos)
            {
                if (item.Name == "MemberwiseClone")
                {
                    cloneMethod = item;
                    break;
                }
            }
            if (cloneMethod != null) return (T)cloneMethod.Invoke(o, null);
            return default(T);
        }

        public static T If<T>(this T t, Predicate<T> predicate, Action<T> action) where T : class
        {
            if (t == null) throw new ArgumentNullException();
            if (predicate(t)) action(t);
            return t;
        }

        public static T If<T>(this T t, Predicate<T> predicate, Func<T, T> func) where T : struct
        {
            return predicate(t) ? func(t) : t;
        }

        public static string If(this string s, Predicate<string> predicate, Func<string, string> func)
        {
            return predicate(s) ? func(s) : s;
        }

        public static TOutput Switch<TOutput, TInput>(this TInput input, IEnumerable<TInput> inputSource, IEnumerable<TOutput> outputSource, TOutput defaultOutput)
        {
            IEnumerator<TInput> inputIterator = inputSource.GetEnumerator();
            IEnumerator<TOutput> outputIterator = outputSource.GetEnumerator();

            TOutput result = defaultOutput;
            while (inputIterator.MoveNext())
            {
                if (outputIterator.MoveNext())
                {
                    if (input.Equals(inputIterator.Current))
                    {
                        result = outputIterator.Current;
                        break;
                    }
                }
                else break;
            }
            return result;
        }

        public static void While<T>(this T t, Predicate<T> predicate, Action<T> action) where T : class
        {
            while (predicate(t)) action(t);
        }

        public static void While<T>(this T t, Predicate<T> predicate, params Action<T>[] actions) where T : class
        {
            while (predicate(t))
            {
                foreach (var action in actions)
                    action(t);
            }
        }

        #endregion

        #region DateExtensions
        private static readonly TimeSpan _OneMinute = new TimeSpan(0, 1, 0);
        private static readonly TimeSpan _TwoMinutes = new TimeSpan(0, 2, 0);
        private static readonly TimeSpan _OneHour = new TimeSpan(1, 0, 0);
        private static readonly TimeSpan _TwoHours = new TimeSpan(2, 0, 0);
        private static readonly TimeSpan _OneDay = new TimeSpan(1, 0, 0, 0);
        private static readonly TimeSpan _TwoDays = new TimeSpan(2, 0, 0, 0);
        private static readonly TimeSpan _OneWeek = new TimeSpan(7, 0, 0, 0);
        private static readonly TimeSpan _TwoWeeks = new TimeSpan(14, 0, 0, 0);
        private static readonly TimeSpan _OneMonth = new TimeSpan(31, 0, 0, 0);
        private static readonly TimeSpan _TwoMonths = new TimeSpan(62, 0, 0, 0);
        private static readonly TimeSpan _OneYear = new TimeSpan(365, 0, 0, 0);
        private static readonly TimeSpan _TwoYears = new TimeSpan(730, 0, 0, 0);

        public static TimeSpan GetTimeSpan(this DateTime startTime, DateTime endTime)
        {
            return endTime - startTime;
        }
        public static string ToDateTime(this DateTime time)
        {
            return time.ToString("yyyy-MM-dd HH:mm:ss");
        }
        public static string ToDateTimeF(this DateTime time)
        {
            return time.ToString("yyyy-MM-dd HH:mm:ss:fffffff");
        }
        public static int CalculateAge(this DateTime dateOfBirth)
        {
            return CalculateAge(dateOfBirth, DateTime.Today);
        }
        public static int CalculateAge(this DateTime dateOfBirth, DateTime referenceDate)
        {
            int years = referenceDate.Year - dateOfBirth.Year;
            if (referenceDate.Month < dateOfBirth.Month || (referenceDate.Month == dateOfBirth.Month && referenceDate.Day < dateOfBirth.Day)) --years;
            return years;
        }
        public static int GetCountDaysOfMonth(this DateTime date)
        {
            var nextMonth = date.AddMonths(1);
            return new DateTime(nextMonth.Year, nextMonth.Month, 1).AddDays(-1).Day;
        }
        public static DateTime GetFirstDayOfMonth(this DateTime date)
        {
            return new DateTime(date.Year, date.Month, 1);
        }
        public static DateTime GetFirstDayOfMonth(this DateTime date, DayOfWeek dayOfWeek)
        {
            var dt = date.GetFirstDayOfMonth();
            while (dt.DayOfWeek != dayOfWeek)
            {
                dt = dt.AddDays(1);
            }
            return dt;
        }
        public static DateTime GetLastDayOfMonth(this DateTime date)
        {
            return new DateTime(date.Year, date.Month, GetCountDaysOfMonth(date));
        }
        public static DateTime GetLastDayOfMonth(this DateTime date, DayOfWeek dayOfWeek)
        {
            var dt = date.GetLastDayOfMonth();
            while (dt.DayOfWeek != dayOfWeek)
            {
                dt = dt.AddDays(-1);
            }
            return dt;
        }
        public static bool IsToday(this DateTime dt)
        {
            return (dt.Date == DateTime.Today);
        }
        public static bool IsToday(this DateTimeOffset dto)
        {
            return (dto.Date.IsToday());
        }
        public static DateTime SetTime(this DateTime date, int hours, int minutes, int seconds)
        {
            return date.SetTime(new TimeSpan(hours, minutes, seconds));
        }
        public static DateTime SetTime(this DateTime date, TimeSpan time)
        {
            return date.Date.Add(time);
        }

        public static DateTime GetNextWeekday(this DateTime date, DayOfWeek weekday)
        {
            while (date.DayOfWeek != weekday) date = date.AddDays(1);
            return date;
        }
        public static DateTime GetPreviousWeekday(this DateTime date, DayOfWeek weekday)
        {
            while (date.DayOfWeek != weekday) date = date.AddDays(-1);
            return date;
        }

        public static string ToAgo(this DateTime date)
        {
            TimeSpan timeSpan = date.GetTimeSpan(DateTime.Now);
            if (timeSpan < TimeSpan.Zero) return "未来";
            if (timeSpan < _OneMinute) return "现在";
            if (timeSpan < _TwoMinutes) return "1 分钟前";
            if (timeSpan < _OneHour) return String.Format("{0} 分钟前", timeSpan.Minutes);
            if (timeSpan < _TwoHours) return "1 小时前";
            if (timeSpan < _OneDay) return String.Format("{0} 小时前", timeSpan.Hours);
            if (timeSpan < _TwoDays) return "昨天";
            if (timeSpan < _OneWeek) return String.Format("{0} 天前", timeSpan.Days);
            if (timeSpan < _TwoWeeks) return "1 周前";
            if (timeSpan < _OneMonth) return String.Format("{0} 周前", timeSpan.Days / 7);
            if (timeSpan < _TwoMonths) return "1 月前";
            if (timeSpan < _OneYear) return String.Format("{0} 月前", timeSpan.Days / 31);
            if (timeSpan < _TwoYears) return "1 年前";

            return String.Format("{0} 年前", timeSpan.Days / 365);
        }
        #endregion

        #region IEnumerableExtensions

        /// <summary>
        /// 扩展foreach循环
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="enumerable"></param>
        /// <param name="action"></param>
        [DebuggerStepThrough]
        public static void Each<T>(this IEnumerable<T> enumerable, Action<T> action)
        {
            if (action == null) return;

            foreach (var item in enumerable)
                action(item);
        }

        public static bool IsNullEmpty(this IEnumerable source)
        {
            if (source == null) return true;
            foreach (var item in source) return false;
            return true;
        }

        public static DataTable ToDataTable<T>(this IEnumerable<T> varlist)
        {
            DataTable dtReturn = new DataTable();
            PropertyInfo[] oProps = null;
            if (varlist == null) return dtReturn;

            foreach (T rec in varlist)
            {
                if (oProps == null)
                {
                    oProps = ((Type)rec.GetType()).GetProperties();
                    foreach (PropertyInfo pi in oProps)
                    {
                        Type colType = pi.PropertyType;
                        if ((colType.IsGenericType) && (colType.GetGenericTypeDefinition() == typeof(Nullable<>))) colType = colType.GetGenericArguments()[0];
                        dtReturn.Columns.Add(new DataColumn(pi.Name, colType));
                    }
                }
                DataRow dr = dtReturn.NewRow();
                foreach (PropertyInfo pi in oProps) dr[pi.Name] = pi.GetValue(rec, null) == null ? DBNull.Value : pi.GetValue(rec, null);
                dtReturn.Rows.Add(dr);
            }
            return dtReturn;
        }

        public static bool IsBetween<T>(this T t, T lowerBound, T upperBound, bool includeLowerBound = false, bool includeUpperBound = false) where T : IComparable<T>
        {
            if (t == null) throw new ArgumentNullException("t");

            var lowerCompareResult = t.CompareTo(lowerBound);
            var upperCompareResult = t.CompareTo(upperBound);

            return (includeLowerBound && lowerCompareResult == 0) ||
                (includeUpperBound && upperCompareResult == 0) ||
                (lowerCompareResult > 0 && upperCompareResult < 0);
        }

        public static bool In<T>(this T t, params T[] c)
        {
            return c.Contains(i => i.Equals(t));
        }

        #endregion

        #region DictionaryExtensions
        /// <summary>
        /// 尝试将键和值添加到字典中：如果不存在，才添加；存在，不添加也不抛导常
        /// </summary>
        public static Dictionary<TKey, TValue> TryAdd<TKey, TValue>(this Dictionary<TKey, TValue> dict, TKey key, TValue value)
        {
            if (dict.ContainsKey(key) == false) dict.Add(key, value);
            return dict;
        }

        /// <summary>
        /// 将键和值添加或替换到字典中：如果不存在，则添加；存在，则替换
        /// </summary>
        public static Dictionary<TKey, TValue> AddOrReplace<TKey, TValue>(this Dictionary<TKey, TValue> dict, TKey key, TValue value)
        {
            dict[key] = value;
            return dict;
        }

        /// <summary>
        /// 获取与指定的键相关联的值，如果没有则返回输入的默认值
        /// </summary>
        public static TValue GetValue<TKey, TValue>(this Dictionary<TKey, TValue> dict, TKey key, TValue defaultValue = default(TValue))
        {
            return dict.ContainsKey(key) ? dict[key] : defaultValue;
        }

        /// <summary>
        /// 向字典中批量添加键值对
        /// </summary>
        /// <param name="replaceExisted">如果已存在，是否替换</param>
        public static Dictionary<TKey, TValue> AddRange<TKey, TValue>(this Dictionary<TKey, TValue> dict, IEnumerable<KeyValuePair<TKey, TValue>> values, bool replaceExisted)
        {
            foreach (var item in values)
            {
                if (dict.ContainsKey(item.Key) == false || replaceExisted)
                    dict[item.Key] = item.Value;
            }
            return dict;
        }
        #endregion

        #region DataTableExtensions
        public static List<TResult> ToList<TResult>(this DataTable dt) where TResult : class,new()
        {
            List<TResult> list = new List<TResult>();
            if (dt == null) return list;
            int len = dt.Rows.Count;

            for (int i = 0; i < len; i++)
            {
                TResult info = new TResult();
                foreach (DataColumn dc in dt.Rows[i].Table.Columns)
                {
                    if (dt.Rows[i][dc.ColumnName] == null || string.IsNullOrEmpty(dt.Rows[i][dc.ColumnName].ToString())) continue;
                    System.Reflection.PropertyInfo pi = info.GetType().GetProperty(dc.ColumnName);
                    pi.SetValue(info, dt.Rows[i][dc.ColumnName], null);
                }
                list.Add(info);
            }
            dt.Dispose(); dt = null;
            return list;
        }
        public static DataTable OrderBy(this DataTable dt, string orderBy)
        {
            dt.DefaultView.Sort = orderBy;
            return dt.DefaultView.ToTable();
        }
        public static DataTable Where(this DataTable dt, string where)
        {
            DataTable resultDt = dt.Clone();
            DataRow[] resultRows = dt.Select(where);
            foreach (DataRow dr in resultRows) resultDt.Rows.Add(dr.ItemArray);
            return resultDt;
        }
        public static List<TResult> OrderBy<TResult>(this DataTable dt, string orderBy) where TResult : class,new()
        {
            return dt.OrderBy(orderBy).ToList<TResult>();
        }
        public static List<TResult> Where<TResult>(this DataTable dt, string where) where TResult : class,new()
        {
            return dt.Where(where).ToList<TResult>();
        }
        public static List<TResult> ToPage<TResult>(this DataTable dt, int pageIndex, int pageSize, out int totalRecords) where TResult : class,new()
        {
            totalRecords = dt.Rows.Count;
            int startRow = (pageIndex - 1) * pageSize;
            int endRow = startRow + pageSize;
            if (startRow > totalRecords || startRow < 0) { startRow = 0; endRow = pageSize; }
            if (endRow > totalRecords + pageSize) { startRow = totalRecords - pageSize; endRow = totalRecords; }

            DataTable dt2 = dt.Clone();
            for (int i = startRow; i < endRow; i++) { if (i >= totalRecords) break; dt2.Rows.Add(dt.Rows[i].ItemArray); }

            return dt2.ToList<TResult>();
        }

        public static T Get<T>(this DataRow row, string field) { return row.Get<T>(field, default(T)); }
        public static T Get<T>(this DataRow row, string field, T defaultValue)
        {
            var value = row[field];
            if (value == DBNull.Value) return defaultValue;
            return value.ConvertTo<T>(defaultValue);
        }
        public static T Get<T>(this DataRowView row, string field) { return row.Get<T>(field, default(T)); }
        public static T Get<T>(this DataRowView row, string field, T defaultValue)
        {
            var value = row[field];
            if (value == DBNull.Value) return defaultValue;
            return value.ConvertTo<T>(defaultValue);
        }

        #endregion

        #region TreeDataExtensions

        public static IEnumerable<T> GetDescendants<T>(this T root, Func<T, IEnumerable<T>> childSelector, Predicate<T> filter = null)
        {
            foreach (T t in childSelector(root))
            {
                if (filter == null || filter(t))
                    yield return t;
                foreach (T child in GetDescendants((T)t, childSelector, filter))
                    yield return child;
            }
        }

        public static IEnumerable<T> GetDescendants<TRoot, T>(this TRoot root, Func<TRoot, IEnumerable<T>> rootChildSelector, Func<T, IEnumerable<T>> childSelector, Predicate<T> filter = null)
        {
            foreach (T t in rootChildSelector(root))
            {
                if (filter == null || filter(t))
                    yield return t;
                foreach (T child in GetDescendants(t, childSelector, filter))
                    yield return child;
            }
        }

        #endregion

        #region 枚举扩展
        /// <summary>
        /// 从枚举中获取Description
        /// 说明：
        /// 单元测试-->通过
        /// </summary>
        /// <param name="enumName">需要获取枚举描述的枚举</param>
        /// <returns>描述内容</returns>
        public static string GetDescription(this Enum enumName)
        {
            string _description = string.Empty;
            FieldInfo _fieldInfo = enumName.GetType().GetField(enumName.ToString());
            DescriptionAttribute[] _attributes = _fieldInfo.GetDescriptAttr();
            if (_attributes != null && _attributes.Length > 0)
                _description = _attributes[0].Description;
            else
                _description = enumName.ToString();
            return _description;
        }

        /// <summary>
        /// 获取字段Description
        /// </summary>
        /// <param name="fieldInfo">FieldInfo</param>
        /// <returns>DescriptionAttribute[] </returns>
        public static DescriptionAttribute[] GetDescriptAttr(this FieldInfo fieldInfo)
        {
            if (fieldInfo != null)
            {
                return (DescriptionAttribute[])fieldInfo.GetCustomAttributes(typeof(DescriptionAttribute), false);
            }
            return null;
        }

        /// <summary>
        /// 根据Description获取枚举
        /// 说明：
        /// 单元测试-->通过
        /// </summary>
        /// <typeparam name="T">枚举类型</typeparam>
        /// <param name="description">枚举描述</param>
        /// <returns>枚举</returns>
        public static T GetEnumName<T>(this string description)
        {
            Type _type = typeof(T);
            foreach (FieldInfo field in _type.GetFields())
            {
                DescriptionAttribute[] _curDesc = field.GetDescriptAttr();
                if (_curDesc != null && _curDesc.Length > 0)
                {
                    if (_curDesc[0].Description == description)
                        return (T)field.GetValue(null);
                }
                else
                {
                    if (field.Name == description)
                        return (T)field.GetValue(null);
                }
            }
            throw new ArgumentException(string.Format("{0} 未能找到对应的枚举.", description), "Description");
        }

        #endregion

        #region 验证扩展
        public static bool IsValid<T>(this T obj,out string errorMsg)
        {
            StringBuilder builder = new StringBuilder();

            List<ValidationResult> errorMessage = new List<ValidationResult>();

            var context = new ValidationContext(obj, serviceProvider: null, items: null);

            bool IsValid = Validator.TryValidateObject(obj, context, errorMessage);

            errorMessage.Each(e => {
                builder.AppendLine(e.ErrorMessage + Environment.NewLine);
            });

            errorMsg = builder.ToString();

            return IsValid;
        }
        #endregion

        #region 日期扩展
        public static string ToyyyyMMddHHmmss(this DateTime dt)
        {
            return dt.ToString("yyyy-MM-dd HH:mm:ss");
        }
        /// <summary>
        /// 将Unix时间戳转换为DateTime类型时间
        /// </summary>
        /// <param name="d">double 型数字</param>
        /// <returns>DateTime</returns>
        public static System.DateTime ConvertIntDateTime(this double d)
        {
            System.DateTime time = System.DateTime.MinValue;
            System.DateTime startTime = TimeZone.CurrentTimeZone.ToLocalTime(new System.DateTime(1970, 1, 1));
            time = startTime.AddMilliseconds(d);
            return time;
        }

        /// <summary>
        /// 将c# DateTime时间格式转换为Unix时间戳格式
        /// </summary>
        /// <param name="time">时间</param>
        /// <returns>long</returns>
        public static long ConvertDateTimeInt(this System.DateTime time)
        {
            //double intResult = 0;
            System.DateTime startTime = TimeZone.CurrentTimeZone.ToLocalTime(new System.DateTime(1970, 1, 1, 0, 0, 0, 0));
            //intResult = (time- startTime).TotalMilliseconds;
            long t = (time.Ticks - startTime.Ticks) / 10000; //除10000调整为13位
            return t;
        }
        #endregion

        #region 文件流扩展

        /// <summary>
        ///文件流转字节数组
        /// </summary>
        /// <param name="stream"></param>
        /// <returns></returns>
        public static byte[] ToByteArray(this Stream stream)
        {
            using (BinaryReader binreader = new BinaryReader(stream))
            {
                return binreader.ReadBytes(stream.Length.ConvertTo<int>());
            }
        }

        #endregion
    }

    /// <summary>
    /// 数据类型
    /// </summary>
    public struct DataType
    {
        /// <summary>
        /// 布尔型
        /// </summary>
        public const string Boolean = "bool";

        /// <summary>
        /// 字符串性
        /// </summary>
        public const string String = "string";

        /// <summary>
        /// 整形数据
        /// </summary>
        public const string Int = "int";
    }
}
